import java.util.ArrayList;
import java.util.Observable;
import java.util.Random;

public class LifeGameModel extends Observable {

	private ArrayList<Cell> _cells;
	private ArrayList<IRule> _rules;
	private int _heigth;
	private int _widht;
	
	public static int LIFE = 50;
	
	private IRule _lifeRule = new IRule() {
		@Override
		public void ApplyRule(Cell cell) {
			if((cell.get_life() > 0) && (!cell.is_alive()))
				cell.set_alive(true);
			if((cell.get_life() == 0) && cell.is_alive())
				cell.set_life(LIFE);
			if(cell.is_alive())
			{
				if(cell.get_neighbor() <= 1)
				{
					if(cell.get_life() > 0)
						cell.set_life(cell.get_life() - 1);
					else
						cell.set_alive(false);
				}
				if(cell.get_neighbor() > 3)
				{
					if(cell.get_life() > 0)
						cell.set_life(cell.get_life() - 1);
					else
						cell.set_alive(false);
				}
			}
			else
			{
				if(cell.get_neighbor() == 3)
				{
					cell.set_alive(true);
					cell.set_life(LIFE);
				}
			}
		}
	};

	public ArrayList<IRule> get_rules() {
		return _rules;
	}

	public void set_rules(ArrayList<IRule> _rules) {
		this._rules = _rules;
	}

	public ArrayList<Cell> get_cells() {
		return _cells;
	}

	public Cell get_cell(int x, int y) {
		return _cells.get((y * _widht) + x);
	}

	public void set_cell(Cell cell, int x, int y) {
		Cell _cell = _cells.get((y * _widht) + x);
		_cell.set_alive(cell.is_alive());
		_cell.set_life(cell.get_life());
		_cell.set_neighbor(cell.get_neighbor());
	}

	public void set_cells(ArrayList<Cell> _cells) {
		this._cells = _cells;
	}

	public int get_heigth() {
		return _heigth;
	}

	public void set_heigth(int _heigth) {
		this._heigth = _heigth;
	}

	public int get_widht() {
		return _widht;
	}

	public void set_widht(int _widht) {
		this._widht = _widht;
	}

	public void Regenerate()
	{
		_cells.clear();
		for(int i = 0; i < (this._widht * this._heigth); i++)
			_cells.add(new Cell());
		Refresh();
	}

	public LifeGameModel()
	{
		this(0,0);
	}

	public LifeGameModel(int widht, int heigth)
	{
		_cells = new ArrayList<Cell>();
		_rules = new ArrayList<IRule>();
		_rules.add(new IRule() {
			@Override
			public void ApplyRule(Cell cell) {
				if(cell.is_alive())
				{
					if(cell.get_neighbor() <= 1)
						cell.set_alive(false);
					if(cell.get_neighbor() > 3)
						cell.set_alive(false);
				}
				else
				{
					if(cell.get_neighbor() == 3)
						cell.set_alive(true);
				}
			}
		});
		for(int i = 0; i < (widht * heigth); i++)
			_cells.add(new Cell());

		_heigth = heigth;
		_widht = widht;
	}

	public void Iterate()
	{

		int[] x_coord = new int[8];
		int[] y_coord = new int[8];
		Cell neighbor;
		Cell c;
		for(int y = 0; y < _heigth; y++)
		{
			for(int x = 0; x < _widht; x++)
			{
				c = _cells.get((y * _widht) + x);

				x_coord[0] = x - 1;
				y_coord[0] = y - 1; 
				x_coord[1] = x - 1;
				y_coord[1] = y;
				x_coord[2] = x - 1;
				y_coord[2] = y + 1;
				x_coord[3] = x;
				y_coord[3] = y - 1;
				x_coord[4] = x;
				y_coord[4] = y + 1;
				x_coord[5] = x + 1;
				y_coord[5] = y - 1;
				x_coord[6] = x + 1;
				y_coord[6] = y;
				x_coord[7] = x + 1;
				y_coord[7] = y + 1;
				c.set_neighbor(0);
				for(int i = 0; i < 8; i++)
				{
					if((x_coord[i] >= 0) &&
							(y_coord[i] >= 0) &&
							(x_coord[i] < _widht) &&
							(y_coord[i] < _heigth))
					{
						neighbor = _cells.get((y_coord[i] * _widht) + x_coord[i]);
						if(neighbor.is_alive())
							c.set_neighbor(c.get_neighbor() + 1);
					}
				}
			}
		}

		for(int y = 0; y < _heigth; y++)
		{
			for(int x = 0; x < _widht; x++)
			{
				c = _cells.get((y * _widht) + x);
				for(int i = 0; i < _rules.size(); i ++)
				{
					_rules.get(i).ApplyRule(c);
				}
			}
		}

		this.setChanged();
		this.notifyObservers();
	}

	public void ClearCells()
	{
		Cell c;
		for(int y = 0; y < get_heigth(); y++)
		{
			for(int x = 0; x < get_widht(); x++)
			{
				c = get_cell(x, y);
				c.set_alive(false);
				c.set_life(0);
			}
		}
	}

	public void toogleCell(int x, int y)
	{
		Cell c = get_cell(x, y);
		c.set_alive(!c.is_alive());
		if(c.is_alive())
			c.set_life(LIFE);
		else
			c.set_life(0);
	}

	public void Genesis()
	{
		Cell c;
		Random r = new Random();
		for(int y = 0; y < get_heigth(); y++)
		{
			for(int x = 0; x < get_widht(); x++)
			{
				c = get_cell(x, y);
				c.set_alive(r.nextBoolean());
				if(c.is_alive())
					c.set_life(0);
				else
					c.set_life(0);
			}
		}
	}

	public void ToogleLifeRule()
	{

		if(_rules.contains(_lifeRule))
			_rules.remove(_lifeRule);
		else
			_rules.add(_lifeRule);
	}

	public void Refresh()
	{
		this.setChanged();
		this.notifyObservers();
	}

}
